/*
 * FitnessComputer.h
 *
 *  Created on: 4.1.2013
 *      Author: shadius
 */

#ifndef FITNESSCOMPUTER_H_
#define FITNESSCOMPUTER_H_
#include "tbb/blocked_range.h"
#include "EvoAlghorithm.h"
#include "IFastDataApprox.h"
#include "Checker.h"
#include "tbb/parallel_for.h"
#include "boost/thread/mutex.hpp"
#include "PoolManager.h"
class FitnessComputer {
	THREAD_RESULTS * const result;
	PoolManager &bgPool;
	PoolManager &igPool;
	//LPAPPROXPOINT const bgAPPROXPOINTS;
	//LPAPPROXPOINT const igAPPROXPOINTS;
	int const len;
	Checker &checker;
	EvoAlghorithm *alg;
	boost::mutex *mutex;
public:
	FitnessComputer(THREAD_RESULTS *result,
			//LPAPPROXPOINT bgAPPROXPOINTS,
			//LPAPPROXPOINT igAPPROXPOINTS,
			PoolManager &bgPool,
			PoolManager &igPool,
			int len, EvoAlghorithm *alg,
			Checker &checker, boost::mutex *mutex) :
			result(result),
			//bgAPPROXPOINTS(bgAPPROXPOINTS),
			//igAPPROXPOINTS(igAPPROXPOINTS),
			bgPool(bgPool),
			igPool(igPool),
			len(len), checker(checker), alg(alg), mutex(mutex) {
	}
	;

	void serial(const int from, const int to, LPAPPROXPOINT bgAPPROXPOINTS,
			LPAPPROXPOINT igAPPROXPOINTS) const {
		double localBestFitness = 0;
		double localbestFitnessIdx = 0;
		double localMinFitness = numeric_limits<double>::infinity();


		for (int i = from; i != to; i++) {
			double fitnessValue = fitness(i, bgAPPROXPOINTS, igAPPROXPOINTS,
					len);
			//cout << "Time Difference Local: " << clock() - StartLocal << endl;

			alg->writeFitness(fitnessValue, i);

			if (fitnessValue > localBestFitness) {
				localBestFitness = fitnessValue;
				localbestFitnessIdx = i;
			} else if (fitnessValue < localMinFitness) {
				localMinFitness = fitnessValue;
			}
		}


		if (result->bestFitness < localBestFitness) {
			result->bestFitness = localBestFitness;
			result->bestFitnessIdx = localbestFitnessIdx;
		} else if (result->minFitness > localMinFitness) {
			result->minFitness = localMinFitness;

		}
	}
//
//	void paralelLambda(const int from, const int to) const {
//
//		tbb::parallel_for(tbb::blocked_range<int>(0, POPULATION_SIZE),
//				[=] (const tbb::blocked_range<int>& r)
//				{
//
//					double localBestFitness = 0;
//					double localbestFitnessIdx = 0;
//					double localMinFitness = numeric_limits<double>::infinity();
//					for (int i = r.begin(); i < r.end(); i++)
//					{
//
//						double fitnessValue = fitness(i, bgAPPROXPOINTS,
//								igAPPROXPOINTS, len);
//						(*(*alg).getFitness())[i] = fitnessValue;
//						if (fitnessValue > localBestFitness) {
//							localBestFitness = fitnessValue;
//							localbestFitnessIdx = i;
//						} else if (fitnessValue < localMinFitness) {
//							localMinFitness = fitnessValue;
//						}
//					}
//
//					if (result->bestFitness < localBestFitness) {
//						result->bestFitness = localBestFitness;
//						result->bestFitnessIdx = localbestFitnessIdx;
//					} else if (result->minFitness > localMinFitness) {
//						result->minFitness = localMinFitness;
//					}
//				});
//		}
//
	void operator()(const tbb::blocked_range<int>& r) const {
		//(*((*alg).getFitness()))[r] =
		double localBestFitness = 0;
		double localbestFitnessIdx = 0;
		double localMinFitness = numeric_limits<double>::infinity();
		//std::cout << "<" << r.begin() << "," << r.end() << endl;
		int end = r.end();
		clock_t StartLocal = clock();
		LPAPPROXPOINT bgAPPROXPOINTS;
		LPAPPROXPOINT igAPPROXPOINTS;
		mutex->lock();
		int indexBg = bgPool.getUniqueMemoryChunkArray(bgAPPROXPOINTS);
		int indexIg = igPool.getUniqueMemoryChunkArray(igAPPROXPOINTS);
		mutex->unlock();
		for (int i = r.begin(); i != end; i++) {


			double fitnessValue = fitness(i, bgAPPROXPOINTS, igAPPROXPOINTS,
					len);
			//cout << "Time Difference Local: " << clock() - StartLocal << endl;
			alg->writeFitness(fitnessValue, i);
			if (fitnessValue > localBestFitness) {
				localBestFitness = fitnessValue;
				localbestFitnessIdx = i;
			} else if (fitnessValue < localMinFitness) {
				localMinFitness = fitnessValue;
			}
		}
		//cout << "Time Difference Parallel: " << clock() - StartLocal << endl;

		{
		boost::lock_guard<boost::mutex> lock(*mutex);

		if (result->bestFitness < localBestFitness) {
			result->bestFitness = localBestFitness;
			result->bestFitnessIdx = localbestFitnessIdx;
		} else if (result->minFitness > localMinFitness) {
			result->minFitness = localMinFitness;

		}

		bgPool.returnUniqueMemoryChunkArray(indexBg);
		igPool.returnUniqueMemoryChunkArray(indexIg);
		}
	}
//
//	virtual ~FitnessComputer();
//
private:
	double fitness(int iInd, LPAPPROXPOINT bgAPPROXPOINTS,
			LPAPPROXPOINT igAPPROXPOINTS, int len) const {

		double p = alg->getPopulation()[iInd].p;//(iInd, PI);
		double cg = alg->getPopulation()[iInd].cg;
		double c = alg->getPopulation()[iInd].c;
		double dt = alg->getPopulation()[iInd].dt;
		double k = alg->getPopulation()[iInd].k;


		int count = 0;
		double sum = 0;

		if (!countAverage(bgAPPROXPOINTS, igAPPROXPOINTS, p, cg, c, dt, k, len,
				&sum, &count)) {
			return 0;
		}

		return 1.0 / (sum / (double) count);

	}

	bool countAverage(LPAPPROXPOINT bgAPPROXPOINTS,
			LPAPPROXPOINT igAPPROXPOINTS, double p, double cg, double c,
			double dt, double k, int len, double* sum, int* count) const {

		double localSum = 0;
		int localCount = 0;
		for (int i = 0; i < len; i++) {

			int igPoint = 0;
			double ecg = (*alg->getEcgValues())[i];
			if (!checker.checkRangeC(c) || !checker.checkRangeCG(cg)
					|| !checker.checkRangeP(p) || !checker.checkRangeDT(dt)
					|| !checker.checkTimeJumpRange(k, dt, ecg)) {
				return false;
			}
			alg->getBgValues()->GetPrecedingPoint(
					TimeUtils::convertSecToWin(i) + dt + k * dt * ecg,
					&igPoint);

			if (igPoint < len && igPoint > 0) {
				double dif = fabs(
						p * bgAPPROXPOINTS[i].y + cg * bgAPPROXPOINTS[i].y * ecg
								+ c - igAPPROXPOINTS[igPoint].y);

				localCount++;
				localSum += dif;
			}

		}
		*count = localCount;
		*sum = localSum;
		return true;
	}

};

#endif /* FITNESSCOMPUTER_H_ */
